package com.honeycomb.article;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.honeycomb.common.NumberIDData;

public class PageView<E extends NumberIDData> {
	
	public static final int MAX_PAGE_COUNT_PER_VIEW = 10;  // each view can contain max pages

	protected Logger logger = LogManager.getLogger(this.getClass());
	
	private String id;
	private int maxDataCountPerPage = 20;
	private int firstPage;
	
	private DataRef firstDataRef;
	private List<E> viewData;
	
	
	private PageView(String id, int maxDataCountPerPage, int firstPage, Long firstDataID) {
		this.id = id;
		this.maxDataCountPerPage = maxDataCountPerPage;
		this.firstPage = firstPage;

		// dataIndex is start from 0, which can be compatible with database limit sql
		this.firstDataRef = new DataRef((firstPage-1)*maxDataCountPerPage );
		if (firstDataID != null) {
			firstDataRef.setDataID(firstDataID);
		}
		
		if (logger.isDebugEnabled()) {
			logger.debug(" PageView is initilized, id=" + id + ", maxDataCountPerPage=" + maxDataCountPerPage + ", firstPage=" + firstPage + ", firstDataRef=" + firstDataRef);
		}
	}
	
	public PageView(int maxDataCountPerPage) {
		this(generateID(), maxDataCountPerPage, 1, null);
	}
	
	private PageView(int maxDataCountPerPage, int firstPage, Long firstDataID) {
		this(generateID(), maxDataCountPerPage, firstPage, firstDataID);
	}
	
	
	private static String generateID() {
		return UUID.randomUUID().toString().replaceAll("-", "");
	}
	
	
	public static PageView decode(String encodeStr) {
		String[] elems = encodeStr.split("-");
		if (elems.length != 4) {
			throw new IllegalArgumentException("The encode string can not be recognized: " + encodeStr);
		}
		
		try {
			String id = elems[0];
			int maxCountPerPage = Integer.parseInt(elems[1]);
			int firstPg = Integer.parseInt(elems[2]);
			Long firstDataID = elems[3].equals(" ") ? null : Long.parseLong(elems[3]);
			
			return new PageView(id, maxCountPerPage, firstPg, firstDataID);
		}
		catch(Exception ex) {
			throw new IllegalArgumentException("The encode string can not be recognized: " + encodeStr);
		}
	}
	
	
	public String encode() {
		return id + "-" + maxDataCountPerPage + "-" + firstPage + "-" + (firstDataRef.getDataID() == null ? " " : firstDataRef.getDataID().longValue());
	}

	public String getID() {
		return id;
	}
	
	public int getFirstPage() {
		return firstPage;
	}

	public int getLastPage() {
		if (viewData == null) {
			throw new IllegalStateException("The last page can not be decided before the data is loaded.");
		}
		
		if (viewData.size() == 0 ) {
			return firstPage;
		}
		else if (viewData.size() <= getMaxAllowedDataCount() ) {
			return firstPage + (viewData.size() - 1) / maxDataCountPerPage;
		}
		else {
			return firstPage + (getMaxAllowedDataCount() - 1) / maxDataCountPerPage;
		}
	}

	public void loadData(List<E> dataList) {
		viewData = dataList;
		firstDataRef.setDataID(dataList.size() > 0 ? viewData.get(0).getId() : null );
	}
	
	public List<E> getPageData(int currentPage) {
		if (viewData == null) {
			throw new IllegalStateException("The page data can not be fetched before the data is loaded.");
		}
		
		if (currentPage < firstPage || currentPage > getLastPage() ) {
			throw new IllegalArgumentException(currentPage + " is not allowed in this PageView page number, it must be between " + firstPage + " and " + getLastPage() );
		}
		
		if (viewData.size() == 0) {
			return new ArrayList<E>(0);
		}
		
		int startIndex = (currentPage - firstPage ) * maxDataCountPerPage;  // Here no need to +1 because the index start from 0
		if (startIndex > viewData.size()-1) {
			logger.warn("getPageData for page " + currentPage + " , startIndex=" + startIndex + " is larger than data size, viewData.size()=" + viewData.size());
			return new ArrayList<E>(0);
		}
		else {
			int endIndexExclusive = startIndex + maxDataCountPerPage > viewData.size() ? viewData.size() : startIndex + maxDataCountPerPage;
			if (logger.isDebugEnabled()) {
				logger.debug("getPageData for page " + currentPage + " , startIndex=" + startIndex + ", endIndexExclusive=" + endIndexExclusive);
			}
			return viewData.subList(startIndex, endIndexExclusive);
		}
	}
	
	public DataRef getFirstDataRef() {
		return firstDataRef;
	}
	
	public int getMaxAllowedDataCount() {
		return MAX_PAGE_COUNT_PER_VIEW * maxDataCountPerPage;
	}
	
	/**
	 * Next view
	 * @return  if there is no data for next view, return null
	 */
	public PageView<E> nextView() {
		if (hasNext()) {
			return new PageView<E>(maxDataCountPerPage, getLastPage() + 1, viewData.get(getMaxAllowedDataCount()).getId() );
		}
		else {
			return null;
		}
	}
	
	public boolean hasNext() {
		if (viewData == null) {
			throw new IllegalStateException("The next View can not be decided before the data is loaded.");
		}
		else if (viewData.size() <= getMaxAllowedDataCount() ) {
			return false;
		}
		else {
			return true;
		}
	}
	
	public PageView<E> previousView() {
		if (hasPrevious()) {
			int previous1stPage = firstPage - MAX_PAGE_COUNT_PER_VIEW > 0 ? firstPage - MAX_PAGE_COUNT_PER_VIEW : 1;
			
			return new PageView<E>(maxDataCountPerPage, previous1stPage, null);
		}
		else {
			return null;
		}
	}
	
	public boolean hasPrevious() {
		if (getFirstPage() > 1) {
			return true;
		}
		else {
			return false;
		}
	}
	
}
